/**
 * 
 */
package com.redhat.auditor.helper;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

/**
 * Utility class containing convenience methods and constants with high
 * potential for reusability.
 * 
 * @version 0.1
 * @author Trungtin Ton
 * 
 */
public class Utils {

	/**
	 * A StringSequence is a readable sequence of strings.
	 * <p>
	 * This class is a specialized implementation of the {@link ArrayList<T>}
	 * class and is provided purely for convenience. It inherits all of the
	 * methods and operations available to {@code ArrayList}, and additionally
	 * provides a few more operations such as {@link #join(CharSequence)} that
	 * act as a shortcut or extension for a number of other string and iterable
	 * functions.
	 * 
	 * @version 0.1
	 * @author Trungtin Ton
	 * 
	 */
	public static class StringSequence extends ArrayList<String> {
		/**
		 * The default number of slots allocated for a string sequence where it
		 * is not explicitly specified.
		 */
		private static final int DEFAULT_CAPACITY = 16;

		/**
		 * Default generated serialization universal identifier for this class.
		 */
		private static final long serialVersionUID = 1L;

		/**
		 * The {@code ArrayList} of strings that provides underlying
		 * implementation of this String Sequence. If not declared, this is
		 * initialized with an initial capacity of 16.
		 */
		private final ArrayList<String> strings;

		/**
		 * Constructs a new empty StringSequence with the default initial
		 * capacity of {@value #DEFAULT_CAPACITY}.
		 */
		public StringSequence() {
			this(DEFAULT_CAPACITY);
		}

		/**
		 * Constructs a new StringSequence initialized with the contents of the
		 * given {@code ArrayList}, in the same order as in the given list.
		 * 
		 * @param s
		 *            the {@code ArrayList} of strings to add to this new
		 *            StringSequence
		 */
		public StringSequence(final ArrayList<String> s) {
			this.strings = s;
		}

		/**
		 * Constructs a new empty StringSequence with the specified initial
		 * capacity.
		 * 
		 * @param capacity
		 *            the initial capacity of this new empty StringSequence
		 */
		public StringSequence(final int capacity) {
			strings = new ArrayList<String>(capacity);
		}

		/**
		 * Constructs a new StringSequence initialized with the contents of the
		 * given array of strings, in the same order as in the given array.
		 * 
		 * @param s
		 *            the array of strings to add to this new StringSequence
		 */
		public StringSequence(final String[] s) {
			this.strings = (ArrayList<String>) Arrays.asList(s);
		}

		/**
		 * @see java.util.ArrayList#add(int, java.lang.Object)
		 */
		@Override
		public void add(final int index, final String element) {
			strings.add(index, element);
		}

		/**
		 * @see java.util.ArrayList#add(java.lang.Object)
		 */
		@Override
		public boolean add(final String e) {
			return strings.add(e);
		}

		/**
		 * @see java.util.ArrayList#addAll(java.util.Collection)
		 */
		@Override
		public boolean addAll(final Collection<? extends String> c) {
			return strings.addAll(c);
		}

		/**
		 * @see java.util.ArrayList#addAll(int, java.util.Collection)
		 */
		@Override
		public boolean addAll(final int index,
				final Collection<? extends String> c) {
			return strings.addAll(index, c);
		}

		/**
		 * @see java.util.ArrayList#clear()
		 */
		@Override
		public void clear() {
			strings.clear();
		}

		/**
		 * @see java.util.ArrayList#clone()
		 */
		@Override
		public Object clone() {
			return strings.clone();
		}

		/**
		 * @see java.util.ArrayList#contains(java.lang.Object)
		 */
		@Override
		public boolean contains(final Object o) {
			return strings.contains(o);
		}

		/**
		 * @see java.util.AbstractCollection#containsAll(java.util.Collection)
		 */
		@Override
		public boolean containsAll(final Collection<?> c) {
			return strings.containsAll(c);
		}

		/**
		 * @see java.util.ArrayList#ensureCapacity(int)
		 */
		@Override
		public void ensureCapacity(final int minCapacity) {
			strings.ensureCapacity(minCapacity);
		}

		/**
		 * @see java.util.AbstractList#equals(java.lang.Object)
		 */
		@Override
		public boolean equals(final Object o) {
			return strings.equals(o);
		}

		/**
		 * @see java.util.ArrayList#get(int)
		 */
		@Override
		public String get(final int index) {
			return strings.get(index);
		}

		/**
		 * @see java.util.AbstractList#hashCode()
		 */
		@Override
		public int hashCode() {
			return strings.hashCode();
		}

		/**
		 * @see java.util.ArrayList#indexOf(java.lang.Object)
		 */
		@Override
		public int indexOf(final Object o) {
			return strings.indexOf(o);
		}

		/**
		 * @see java.util.ArrayList#isEmpty()
		 */
		@Override
		public boolean isEmpty() {
			return strings.isEmpty();
		}

		/**
		 * @see java.util.AbstractList#iterator()
		 */
		@Override
		public Iterator<String> iterator() {
			return strings.iterator();
		}

		/**
		 * Joins this sequence of strings together around the given expression.
		 * The resulting string will be each of the given strings in order, each
		 * separated by the given expression.
		 * <p>
		 * Returns {@code null} if this is an empty sequence of strings; i.e.
		 * there are no strings to join.
		 * 
		 * @param expression
		 *            the expression by which to separate each of the strings
		 *            being joined together.
		 * @return the resulting joined string; {@code null} if this is an empty
		 *         string sequence.
		 */
		public String join(final CharSequence expression) {
			final int k = strings.size();
			if (k == 0)
				return null;
			final StringBuilder out = new StringBuilder();
			out.append(strings.get(0));
			for (int x = 1; x < k; ++x)
				out.append(expression).append(strings.get(x));
			return out.toString();
		}

		/**
		 * @see java.util.ArrayList#lastIndexOf(java.lang.Object)
		 */
		@Override
		public int lastIndexOf(final Object o) {
			return strings.lastIndexOf(o);
		}

		/**
		 * @see java.util.AbstractList#listIterator()
		 */
		@Override
		public ListIterator<String> listIterator() {
			return strings.listIterator();
		}

		/**
		 * @see java.util.AbstractList#listIterator(int)
		 */
		@Override
		public ListIterator<String> listIterator(final int index) {
			return strings.listIterator(index);
		}

		/**
		 * @see java.util.ArrayList#remove(int)
		 */
		@Override
		public String remove(final int index) {
			return strings.remove(index);
		}

		/**
		 * @see java.util.ArrayList#remove(java.lang.Object)
		 */
		@Override
		public boolean remove(final Object o) {
			return strings.remove(o);
		}

		/**
		 * @see java.util.AbstractCollection#removeAll(java.util.Collection)
		 */
		@Override
		public boolean removeAll(final Collection<?> c) {
			return strings.removeAll(c);
		}

		/**
		 * @see java.util.AbstractCollection#retainAll(java.util.Collection)
		 */
		@Override
		public boolean retainAll(final Collection<?> c) {
			return strings.retainAll(c);
		}

		/**
		 * @see java.util.ArrayList#set(int, java.lang.Object)
		 */
		@Override
		public String set(final int index, final String element) {
			return strings.set(index, element);
		}

		/**
		 * @see java.util.ArrayList#size()
		 */
		@Override
		public int size() {
			return strings.size();
		}

		/**
		 * @see java.util.AbstractList#subList(int, int)
		 */
		@Override
		public List<String> subList(final int fromIndex, final int toIndex) {
			return strings.subList(fromIndex, toIndex);
		}

		/**
		 * @see java.util.ArrayList#toArray()
		 */
		@Override
		public Object[] toArray() {
			return strings.toArray();
		}

		/**
		 * @see java.util.ArrayList#toArray(T[])
		 */
		@Override
		public <T> T[] toArray(final T[] a) {
			return strings.toArray(a);
		}

		/**
		 * @return
		 * @see java.util.AbstractCollection#toString()
		 */
		@Override
		public String toString() {
			return strings.toString();
		}

		/**
		 * @see java.util.ArrayList#trimToSize()
		 */
		@Override
		public void trimToSize() {
			strings.trimToSize();
		}
	}

	/**
	 * Joins the given comma-separated list (or array) of strings together
	 * around the given expression. The resulting string will be each of the
	 * given strings in order, each separated by the given expression.
	 * <p>
	 * Returns {@code null} if there are no strings to be joined.
	 * 
	 * @param s
	 *            either an array of strings, or a comma-separated list of
	 *            string arguments to be joined together around the given
	 *            expression.
	 * @param expression
	 *            the expression by which to separate each of the given strings
	 *            being joined together.
	 * @return the resulting joined string; {@code null} if there are no strings
	 *         to join.
	 */
	public static String join(final CharSequence expression, final String... s) {
		final int k = s.length;
		if (k == 0)
			return null;
		final StringBuilder out = new StringBuilder();
		out.append(s[0]);
		for (int x = 1; x < k; ++x)
			out.append(expression).append(s[x]);
		return out.toString();
	}
}
